package com.example.matchingengine.service;

import com.example.matchingengine.model.Order;
import com.example.matchingengine.service.event.TradeEvent;
import com.example.matchingengine.service.model.OrderBook;
import com.example.matchingengine.service.model.OrderBookSnapshot;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 持久化服务
 * 负责WAL（预写日志）和快照持久化功能
 * 确保撮合引擎数据的安全性和可恢复性
 */
@Slf4j
@Service
public class PersistenceService {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * -- GETTER --
     * 检查持久化是否启用
     */
    // 持久化配置
    @Getter
    @Value("${matching-engine.persistence.enabled:true}")
    private boolean persistenceEnabled;

    @Value("${matching-engine.persistence.path:./data/orderbooks}")
    private String persistencePath;

    /**
     * -- GETTER --
     * 检查WAL是否启用
     */
    // WAL配置
    @Getter
    @Value("${matching-engine.persistence.wal.enabled:true}")
    private boolean walEnabled;

    @Value("${matching-engine.persistence.wal.path:./data/orderbooks/wal}")
    private String walPath;

    // 日期时间格式化器 - 用于WAL文件名
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");

    // 当前WAL文件路径
    private String currentWalFilePath;


    /**
     * -- GETTER --
     * 获取WAL写入器（用于OrderBook设置依赖）
     */
    // WAL写入器
    @Getter
    private BufferedWriter walWriter;

    @PostConstruct
    public void init() {
        if (!persistenceEnabled) {
            log.info("持久化功能已禁用");
            return;
        }

        // 创建持久化目录
        File persistenceDir = new File(persistencePath);
        if (!persistenceDir.exists()) {
            persistenceDir.mkdirs();
            log.info("创建持久化目录: {}", persistencePath);
        }

        // 初始化WAL目录
        if (walEnabled) {
            File walDir = new File(walPath);
            if (!walDir.exists()) {
                walDir.mkdirs();
                log.info("创建WAL目录: {}", walPath);
            }

            // 初始化WAL写入器
            initWalWriter();
        }

        log.info("持久化服务初始化完成，持久化已{}, WAL已{}",
                persistenceEnabled ? "启用" : "禁用",
                walEnabled ? "启用" : "禁用");
    }

    @PreDestroy
    public void destroy() {
        if (walWriter != null) {
            try {
                walWriter.flush();
                walWriter.close();
                log.info("WAL写入器已关闭");
            } catch (IOException e) {
                log.error("关闭WAL写入器失败: {}", e.getMessage(), e);
            }
        }
    }

    /**
     * 持久化订单簿数据
     *
     * @param orderBooks     订单簿映射
     * @param orderBooksLock 订单簿读写锁
     */
    public void persistOrderBooksData(Map<String, OrderBook> orderBooks, ReentrantReadWriteLock orderBooksLock) {
        if (!persistenceEnabled) {
            return;
        }

        try {
            log.info("开始持久化订单簿数据...");
            Map<String, OrderBookSnapshot> snapshots = new HashMap<>();

            // 获取读锁，保证数据一致性
            orderBooksLock.readLock().lock();
            try {
                // 为每个交易对创建快照
                for (Map.Entry<String, OrderBook> entry : orderBooks.entrySet()) {
                    String symbol = entry.getKey();
                    OrderBook orderBook = entry.getValue();
                    OrderBookSnapshot snapshot = orderBook.createSnapshot();
                    snapshots.put(symbol, snapshot);
                }
            } finally {
                orderBooksLock.readLock().unlock();
            }

            // 将快照写入临时文件，然后原子重命名，确保文件完整性
            File tempFile = new File(persistencePath + "/orderbooks.json.tmp");
            objectMapper.writeValue(tempFile, snapshots);

            File snapshotFile = new File(persistencePath + "/orderbooks.json");
            Files.move(tempFile.toPath(), snapshotFile.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);

            // 如果启用了WAL，创建新的WAL文件
            if (walEnabled) {
                rotateWalFile();
            }

            log.info("订单簿数据持久化完成，保存至: {}", snapshotFile.getAbsolutePath());
        } catch (Exception e) {
            log.error("订单簿数据持久化失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 从磁盘加载订单簿数据
     *
     * @return 快照数据映射，如果加载失败返回null
     */
    public Map<String, OrderBookSnapshot> loadOrderBooksFromDisk() {
        if (!persistenceEnabled) {
            return null;
        }

        File snapshotFile = new File(persistencePath + "/orderbooks.json");
        if (!snapshotFile.exists()) {
            log.info("未找到订单簿快照文件，将创建新的订单簿");
            return null;
        }

        try {
            log.info("开始从快照文件恢复订单簿数据: {}", snapshotFile.getAbsolutePath());

            // 从文件读取快照数据
            Map<String, OrderBookSnapshot> snapshots = objectMapper.readValue(
                    snapshotFile,
                    new TypeReference<Map<String, OrderBookSnapshot>>() {
                    }
            );

            log.info("订单簿数据恢复完成，共加载{}个交易对", snapshots.size());
            return snapshots;
        } catch (IOException e) {
            log.error("从快照文件恢复订单簿数据失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 应用WAL日志进行增量恢复
     *
     * @param orderBooks 订单簿映射
     */
    public void applyWalLogs(Map<String, OrderBook> orderBooks) {
        if (!walEnabled || !persistenceEnabled) {
            return;
        }

        try {
            // 获取最新的快照时间
            File snapshotFile = new File(persistencePath + "/orderbooks.json");
            if (!snapshotFile.exists()) {
                log.info("未找到快照文件，跳过WAL日志应用");
                return;
            }

            LocalDateTime snapshotTime = LocalDateTime.ofInstant(
                    Files.getLastModifiedTime(snapshotFile.toPath()).toInstant(),
                    java.time.ZoneId.systemDefault());

            // 查找所有WAL文件
            File walDir = new File(walPath);
            File[] walFiles = walDir.listFiles((dir, name) -> name.startsWith("wal_") && name.endsWith(".log"));

            if (walFiles == null || walFiles.length == 0) {
                log.info("未找到WAL日志文件，跳过增量恢复");
                return;
            }

            // 按文件名排序（包含时间戳）
            List<File> sortedWalFiles = new ArrayList<>(List.of(walFiles));
            sortedWalFiles.sort(Comparator.comparing(File::getName));

            // 应用每个WAL文件中的日志
            for (File walFile : sortedWalFiles) {
                // 检查WAL文件是否比快照新
                LocalDateTime walTime = LocalDateTime.ofInstant(
                        Files.getLastModifiedTime(walFile.toPath()).toInstant(),
                        java.time.ZoneId.systemDefault());

                if (walTime.isBefore(snapshotTime)) {
                    log.info("删除旧的WAL文件: {}", walFile.getName());
                    try {
                        Files.delete(walFile.toPath());
                        log.info("成功删除旧的WAL文件: {}", walFile.getName());
                    } catch (IOException e) {
                        log.error("删除旧的WAL文件失败: {}, 错误: {}", walFile.getName(), e.getMessage());
                    }
                    continue;
                }

                log.info("应用WAL日志文件: {}", walFile.getName());

                // 读取并应用WAL日志
                List<String> lines = Files.readAllLines(walFile.toPath());
                for (String line : lines) {
                    applyWalLogEntry(line, orderBooks);
                }
            }

            log.info("WAL日志应用完成，订单簿已恢复到最新状态");
        } catch (Exception e) {
            log.error("应用WAL日志失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 应用单条WAL日志条目
     */
    private void applyWalLogEntry(String line, Map<String, OrderBook> orderBooks) {
        try {
            String[] parts = line.split("\\|", 3);
            if (parts.length != 3) {
                log.warn("无效的WAL日志行: {}", line);
                return;
            }

            String timestamp = parts[0];
            String operation = parts[1];
            String data = parts[2];

            // 根据操作类型应用日志
            switch (operation) {
                case "ADD_ORDER":
                    Order addOrder = objectMapper.readValue(data, Order.class);
                    applyAddOrderLog(addOrder, orderBooks);
                    break;
                case "UPDATE_ORDER":
                    Order updateOrder = objectMapper.readValue(data, Order.class);
                    applyUpdateOrderLog(updateOrder, orderBooks);
                    break;
                case "REMOVE_ORDER":
                    Order removeOrder = objectMapper.readValue(data, Order.class);
                    applyRemoveOrderLog(removeOrder, orderBooks);
                    break;
                case "TRADE":
                    TradeEvent tradeEvent = objectMapper.readValue(data, TradeEvent.class);
                    applyTradeLog(tradeEvent);
                    break;
                default:
                    log.warn("未知的WAL操作类型: {}", operation);
            }
        } catch (Exception e) {
            log.error("应用WAL日志条目失败: {}, 日志行: {}", e.getMessage(), line);
        }
    }

    /**
     * 应用添加订单的WAL日志
     */
    private void applyAddOrderLog(Order order, Map<String, OrderBook> orderBooks) {
        OrderBook orderBook = orderBooks.get(order.getSymbol());
        if (orderBook != null) {
            orderBook.addOrder(order);
            log.debug("应用WAL日志：添加订单 {}", order.getId());
        }
    }

    /**
     * 应用更新订单的WAL日志
     */
    private void applyUpdateOrderLog(Order order, Map<String, OrderBook> orderBooks) {
        OrderBook orderBook = orderBooks.get(order.getSymbol());
        if (orderBook != null) {
            orderBook.updateOrder(order);
            log.debug("应用WAL日志：更新订单 {}", order.getId());
        }
    }

    /**
     * 应用移除订单的WAL日志
     */
    private void applyRemoveOrderLog(Order order, Map<String, OrderBook> orderBooks) {
        OrderBook orderBook = orderBooks.get(order.getSymbol());
        if (orderBook != null) {
            orderBook.removeOrder(order);
            log.debug("应用WAL日志：移除订单 {}", order.getId());
        }
    }

    /**
     * 应用成交事件的WAL日志
     */
    private void applyTradeLog(TradeEvent tradeEvent) {
        // 成交事件通常不需要特殊处理，因为订单状态更新已经包含了成交信息
        log.debug("应用WAL日志：成交事件 买单:{} 卖单:{}", tradeEvent.getBuyOrderId(), tradeEvent.getSellOrderId());
    }

    /**
     * 写入WAL日志
     *
     * @param operation 操作类型（ADD_ORDER, UPDATE_ORDER, REMOVE_ORDER, TRADE）
     * @param data      操作数据（JSON格式）
     */
    public void writeWalLog(String operation, String data) {
        if (!walEnabled || walWriter == null) {
            return;
        }

        try {
            // WAL日志格式：时间戳|操作类型|数据
            String timestamp = LocalDateTime.now().toString();
            String logEntry = timestamp + "|" + operation + "|" + data + "\n";

            // 写入并立即刷新到磁盘
            walWriter.write(logEntry);
            walWriter.flush();
        } catch (IOException e) {
            log.error("写入WAL日志失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 初始化WAL写入器
     */
    private void initWalWriter() {
        if (!walEnabled) {
            return;
        }

        try {
            // 创建新的WAL文件
            String timestamp = LocalDateTime.now().format(DATE_TIME_FORMATTER);
            String walFileName = "wal_" + timestamp + "_" + UUID.randomUUID().toString().substring(0, 8) + ".log";
            currentWalFilePath = walPath + "/" + walFileName;

            // 创建WAL文件对象和写入器
            walWriter = new BufferedWriter(new FileWriter(currentWalFilePath, true));

            log.info("WAL写入器初始化完成，当前WAL文件: {}", currentWalFilePath);
        } catch (IOException e) {
            log.error("初始化WAL写入器失败: {}", e.getMessage(), e);
            walEnabled = false;
        }
    }

    /**
     * 轮换WAL文件
     * 在每次快照持久化后调用，创建新的WAL文件
     */
    private void rotateWalFile() {
        if (!walEnabled || walWriter == null) {
            return;
        }

        try {
            // 关闭当前WAL写入器
            walWriter.flush();
            walWriter.close();

            // 创建新的WAL文件和写入器
            initWalWriter();
        } catch (IOException e) {
            log.error("轮换WAL文件失败: {}", e.getMessage(), e);
        }
    }


}